home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Datafile PD-CD 1 Issue 2
/
PDCD-1 - Issue 02.iso
/
_utilities
/
utilities
/
003
/
_gs
/
!GS
/
c
/
ZFILE
< prev
next >
Wrap
Text File
|
1991-10-27
|
16KB
|
587 lines
/* Copyright (C) 1989, 1990, 1991 Aladdin Enterprises. All rights reserved.
Distributed by Free Software Foundation, Inc.
This file is part of Ghostscript.
Ghostscript is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
to anyone for the consequences of using it or for whether it serves any
particular purpose or works at all, unless he says so in writing. Refer
to the Ghostscript General Public License for full details.
Everyone is granted permission to copy, modify and redistribute
Ghostscript, but only under the conditions described in the Ghostscript
General Public License. A copy of this license is supposed to have been
given to you along with Ghostscript so you can know your rights and
responsibilities. It should be in a file named COPYING. Among other
things, the copyright notice and this notice must be preserved on all
copies. */
/* zfile.c */
/* Non-I/O file operators for Ghostscript */
#include "memory_.h"
#include "string_.h"
#include "ghost.h"
#include "gp.h"
#include "errors.h"
#include "oper.h"
#include "alloc.h"
#include "estack.h" /* for filenameforall */
#include "iutil.h"
#include "save.h" /* for restore */
#include "stream.h"
#include "file.h" /* must come after stream.h */
#include "store.h"
/* Forward references */
int lib_file_open(P3(byte *, uint, ref *));
int file_open(P5(byte *, uint, char *, ref *, stream **));
int file_close(P2(ref *, stream *));
private int open_std_file(P3(os_ptr, char *, os_ptr));
/* In zfileio.c */
ref *zget_current_file(P0());
/* Imported from gs.c */
extern char **gs_lib_paths; /* search path list, */
/* terminated by a null pointer */
/* The chain of all open files. We need this only */
/* so that we can do the right thing for restore (and GC someday). */
private ref all_files; /* t_file */
#define file_list all_files.value.pfile
/* File buffer sizes. For real files, this is arbitrary, */
/* since the C library does its own buffering in addition. */
/* stdout and stderr use smaller buffers, */
/* on the assumption that they are usually not real files. */
/* The buffer size for type 1 encrypted files is NOT arbitrary: */
/* it must be at most 512. */
#define buffer_size 512
/* Standard file objects: */
/* 0 is stdin, 1 is stdout, 2 is stderr, 3 is lineedit, 4 is statementedit */
#define num_std_files 5
stream invalid_file_entry;
private byte
#define stdin_buf_size 1
stdin_buf[stdin_buf_size],
#define stdout_buf_size 128
stdout_buf[stdout_buf_size],
#define stderr_buf_size 128
stderr_buf[stderr_buf_size],
#define lineedit_buf_size 160
lineedit_buf[lineedit_buf_size];
/* statementedit is equivalent to lineedit for now */
stream std_files[num_std_files];
private char *std_file_names[num_std_files] =
{ "%stdin",
"%stdout",
"%stderr",
"%lineedit",
"%statementedit"
};
private int std_file_attrs[num_std_files] =
{ a_read+a_execute,
a_write+a_execute,
a_write+a_execute,
a_read+a_execute,
a_read+a_execute
};
#ifdef ARC
/* On the Archimedes, we often want to replace files with names like x.y.z by
x.z.y. This function checks to see whether a file has one or a range of standard
extensions (z), and makes the change. The result is placed in a given buffer.
The typedef gives us a suitable temporary buffer.
*/
#define ARC_MAX_FILE_NAME 4096 /* A likely value */
typedef char arc_buf[ARC_MAX_FILE_NAME];
private struct arc_ext
{
char *ext;
int len;
} arc_extension[] =
{ /* Shortest must be first */
"ps", 2,
"gsf", 3,
"", 0
};
/* Change and copy name. To and from must not overlap. */
private void arc_cvt_name(char *to, char *from, int len)
{
struct arc_ext *ext;
int elen;
to[len] = 0;
for (ext = arc_extension ; (elen = ext->len) != 0 ; ext++)
{
if (from[len-elen-1] == '.' && memcmp(ext->ext, from+len-elen, elen) == 0)
{
/* Look back for earlier '.' */
int i;
for (i = len-elen-2 ; i >= 0 ; i--)
{
if (from[i] == '.')
{
/* Copy up to and including "." */
memcpy(to, from, i+1);
break;
}
}
/* Add "ext." */
i += 1;
memcpy(to+i, ext->ext, elen);
to[i+elen] = '.';
/* Copy chunk to go after "ext." */
elen += 1;
memcpy(to+i+elen, from+i, len-i-elen);
return;
}
}
/* No extension matches - simple copy */
memcpy(to, from, len);
}
#endif
/* Initialize the file table */
private void
zfile_init()
{ /* Create files for stdin, stdout, and stderr. */
/****** stdin IS NOT IMPLEMENTED PROPERLY ******/
sread_file(&std_files[0], stdin, stdin_buf, stdin_buf_size);
std_files[0].can_close = 0;
swrite_file(&std_files[1], stdout, stdout_buf, stdout_buf_size);
std_files[1].can_close = 0;
swrite_file(&std_files[2], stderr, stderr_buf, stderr_buf_size);
std_files[2].can_close = 0;
make_tav(&all_files, t_file, 0, pfile, (stream *)0);
s_disable(&invalid_file_entry);
}
/* file */
int
zfile(register os_ptr op)
{ char *file_access;
ref fname;
int code;
stream *s;
fname = op[-1];
check_type(fname, t_string);
check_type(*op, t_string);
if ( r_size(op) != 1 ) return e_invalidfileaccess;
switch ( *op->value.bytes )
{
case 'r': file_access = "r"; break;
case 'w': file_access = "w"; break;
default: return e_invalidfileaccess;
}
code = open_std_file(op - 1, file_access, op - 1);
switch ( code )
{
case 0: /* successful open */
pop(1);
default: /* unsuccessful open */
return code;
case e_undefinedfilename: /* not a %file */
;
}
code = file_open(fname.value.bytes, r_size(&fname),
file_access, op - 1, &s);
if ( code >= 0 ) pop(1);
return code;
}
/* closefile */
int
zclosefile(register os_ptr op)
{ stream *s;
int code;
check_file(s, op);
if ( s->can_close == 0 )
return e_invalidaccess; /* can't close std file */
if ( (code = file_close(op, s)) >= 0 )
{ /* If we just closed the file from which the interpreter */
/* is reading, zap it on the exec stack. */
ref *fp = zget_current_file();
if ( fp != 0 && fptr(fp) == fptr(op) )
/* A null would confuse the estack parser.... */
make_tasv(fp, t_array, a_executable+a_execute, 0, refs, (ref *)0);
pop(1);
}
return code;
}
/* ------ Level 2 extensions ------ */
/* deletefile */
int
zdeletefile(register os_ptr op)
{ char *str;
int stat;
check_read_type(*op, t_string);
str = ref_to_string(op, "deletefile");
if ( str == 0 ) return e_VMerror;
#ifdef ARC
{
arc_buf temp_name;
arc_cvt_name(temp_name, str, strlen(str));
stat = remove(temp_name);
}
#else
stat = unlink(str);
#endif
alloc_free(str, r_size(op) + 1, 1, "deletefile");
if ( stat != 0 ) return e_ioerror;
pop(1);
return 0;
}
/* filenameforall */
private int file_continue(P1(os_ptr));
int
zfilenameforall(register os_ptr op)
{ file_enum *pfen;
check_write_type(*op, t_string);
check_proc(op[-1]);
check_read_type(op[-2], t_string);
/* Push a mark, the pattern, the scratch string, the enumerator, */
/* and the procedure, and invoke the continuation. */
check_estack(7);
pfen = gp_enumerate_files_init((char *)op[-2].value.bytes, r_size(op - 2), alloc, alloc_free);
if ( pfen == 0 ) return e_VMerror;
mark_estack(es_for);
*++esp = op[-2];
*++esp = *op;
++esp;
make_tasv(esp, t_string, a_read+a_execute+a_executable, 0,
bytes, (byte *)pfen);
*++esp = op[-1];
pop(3); op -= 3;
return file_continue(op);
}
/* Continuation operator for enumerating files */
private int
file_continue(register os_ptr op)
{ es_ptr pscratch = esp - 2;
file_enum *pfen = (file_enum *)esp[-1].value.bytes;
uint len = r_size(pscratch);
uint code = gp_enumerate_files_next(pfen, (char *)pscratch->value.bytes, len);
if ( code == ~(uint)0 ) /* all done */
{ gp_enumerate_files_close(pfen);
esp -= 4; /* pop mark, scatch, pfen, proc */
}
else if ( code > len ) /* overran string */
{ gp_enumerate_files_close(pfen);
return e_rangecheck;
}
else if ( !string_match(pscratch->value.bytes, code,
pscratch[-1].value.bytes, r_size(&pscratch[-1]))
)
{ /* Enumerator was too liberal, ignore this one. */
push_op_estack(file_continue); /* come again */
}
else
{ push(1);
ref_assign(op, pscratch);
r_set_subrange_size(op, code);
push_op_estack(file_continue); /* come again */
*++esp = pscratch[2]; /* proc */
}
return o_check_estack;
}
/* renamefile */
int
zrenamefile(register os_ptr op)
{ char *str1 = 0, *str2 = 0;
check_read_type(*op, t_string);
check_read_type(op[-1], t_string);
str1 = ref_to_string(op - 1, "renamefile(from)");
#ifdef ARC
if ( str1 != 0 && str2 != 0 )
{
arc_buf temp1, temp2;
arc_cvt_name(temp1, str1, strlen(str1));
arc_cvt_name(temp2, str2, strlen(str2));
if ( rename(temp1, temp2) )
{ pop(2);
}
}
#else
str2 = ref_to_string(op, "renamefile(to)");
if ( str1 != 0 && str2 != 0 && rename(str1, str2) == 0 )
{ pop(2);
}
#endif
if ( str1 != 0 )
alloc_free(str1, r_size(op - 1) + 1, 1, "renamefile(from)");
if ( str2 != 0 )
alloc_free(str2, r_size(op) + 1, 1, "renamefile(to)");
return 0;
}
/* ------ Ghostscript extensions ------ */
/* findlibfile */
int
zfindlibfile(register os_ptr op)
{ int code;
check_type(*op, t_string);
code = open_std_file(op, "r", op - 1);
switch ( code )
{
case 0: /* successful open */
push(1);
make_bool(op, 1);
default: /* unsuccessful open */
return code;
case e_undefinedfilename: /* not a %file */
;
}
code = lib_file_open(op->value.bytes, r_size(op), op);
push(1);
make_bool(op, code >= 0);
return 0;
}
/* type1decryptfile */
int
ztype1decryptfile(register os_ptr op)
{ stream *s;
ushort state;
ref dec_file;
int code;
stream *es;
check_type(op[-1], t_integer);
state = op[-1].value.intval;
if ( op[-1].value.intval != state )
return e_rangecheck; /* state value was truncated */
check_read_file(s, op);
code = file_open((byte *)0, 0, "r", &dec_file, &es);
if ( code < 0 ) return code;
sread_decrypt(es, fptr(op), es->cbuf, es->bsize, state);
op[-1] = dec_file;
pop(1);
return 0;
}
/* ------ Initialization procedure ------ */
op_def zfile_op_defs[] = {
{"1closefile", zclosefile},
{"1deletefile", zdeletefile},
{"2file", zfile},
{"3filenameforall", zfilenameforall},
{"1findlibfile", zfindlibfile},
{"2renamefile", zrenamefile},
{"2type1decryptfile", ztype1decryptfile},
op_def_end(zfile_init)
};
/* ------ Non-operator routines ------ */
/* Open a file, using the search paths if necessary. */
/* The startup code calls this to open the initialization file ghost.ps, */
/* and any other files specified on the command line. */
int
lib_file_open(byte *fname, uint len, ref *pfile)
{ int code;
char **ppath;
#define max_len 200
char cname[max_len];
stream *s;
code = file_open(fname, len, "r", pfile, &s);
if ( code >= 0 ) return code;
if ( gp_file_name_is_absolute((char *)fname, len) )
return e_undefinedfilename;
/* Go through the list of search paths */
for ( ppath = gs_lib_paths; *ppath != 0; ppath++ )
{ char *path = *ppath;
for ( ; ; )
{ /* Find the end of the next path */
char *npath = path;
uint plen;
char *cstr;
uint clen;
while ( *npath != 0 && *npath != gp_file_name_list_separator )
npath++;
plen = npath - path;
cstr = gp_file_name_concat_string(path, plen,
(char *)fname, len);
/* Concatenate the prefix, combiner, and file name. */
clen = plen + strlen(cstr) + len;
if ( clen <= max_len ) /* otherwise punt */
{ memcpy(cname, path, plen);
strcpy(cname + plen, cstr);
memcpy(cname + clen - len, fname, len);
code = file_open((byte *)cname, clen, "r",
pfile, &s);
if ( code >= 0 ) return code;
}
/****** NYI ******/
if ( !*npath ) break;
path = npath + 1;
}
}
return code;
}
/* Open a file and create a file object. */
/* Return 0 if successful, error code if not. */
/* If fname==0, set up the file entry, stream, and buffer, */
/* but don't open an OS file or initialize the stream. */
/* The filter routines also use this. */
int
file_open(byte *fname, uint len, char *file_access, ref *pfile, stream **ps)
{ byte *buffer;
stream *s;
int code;
if ( len >= buffer_size )
return e_limitcheck; /* we copy the file name into the buffer */
/* Allocate the stream first, since it persists */
/* even after the file has been closed. */
s = (stream *)alloc(1, sizeof(stream), "file_open(stream)");
if ( s == 0 ) return e_VMerror;
/* Allocate the buffer. */
buffer = (byte *)alloc(buffer_size, 1, "file_open(buffer)");
if ( buffer == 0 )
{ alloc_free((char *)s, 1, sizeof(stream), "file_open(stream)");
return e_VMerror;
}
if ( fname != 0 )
{ /* Copy the name (so we can terminate it with a zero byte.) */
char *file_name = (char *)buffer;
FILE *file;
memcpy(file_name, fname, len);
file_name[len] = 0; /* terminate string */
/* Open the file. */
#ifdef ARC
{
arc_buf temp_name;
arc_cvt_name(temp_name, file_name, len);
file = fopen(temp_name, file_access);
}
#else
file = fopen(file_name, file_access);
#endif
code = e_undefinedfilename;
if ( file == 0 )
{ alloc_free((char *)buffer, buffer_size, 1, "file_open(buffer)");
alloc_free((char *)s, 1, sizeof(stream), "file_open(stream)");
return code;
}
/* Set up the stream. */
if ( *file_access == 'r' ) /* reading */
sread_file(s, file, buffer, buffer_size);
else
swrite_file(s, file, buffer, buffer_size);
}
else /* save the buffer and size */
{ s->cbuf = buffer;
s->bsize = buffer_size;
}
s->can_close = 1;
if ( file_list != 0 )
file_list->prev = s;
s->next = file_list;
s->prev = 0;
file_list = s;
make_file(pfile,
(*file_access == 'r' ? a_read+a_execute : a_write+a_execute),
s);
*ps = s;
return 0;
}
/* Close a file. The interpreter calls this when */
/* it reaches the end of an executable file. */
int
file_close(ref *fp /* t_file */, stream *s)
{ byte *buffer = s->cbuf;
switch ( s->can_close )
{
case 0: /* can't close std files, ignore */
break;
case -1: /* ignore on statement/lineedit */
sclose(s);
break;
default: /* ordinary or filter file */
if ( sclose(s) ) return e_ioerror;
/* Free the stream and buffer in the reverse of the order */
/* in which they were created, and hope for LIFO storage behavior. */
alloc_free((char *)buffer, buffer_size, 1, "file_close(buffer)");
alloc_free((char *)s, 1, sizeof(stream), "file_close(stream)");
/* Unlink the file from the list of all files. */
if ( s->prev != 0 ) s->prev->next = s->next;
else file_list = s->next;
if ( s->next != 0 ) s->next->prev = s->prev;
}
s_disable(s);
return 0;
}
/* ------ Internal routines ------ */
/* If a file name refers to one of the standard %files, */
/* 'open' the file and return 0 or an error code, otherwise */
/* return e_undefinedfilename. */
private int
open_std_file(os_ptr pfname, char *file_access, os_ptr pfile)
{ int i;
for ( i = 0; i < num_std_files; i++ )
if ( !bytes_compare(pfname->value.bytes, r_size(pfname),
(byte *)std_file_names[i],
strlen(std_file_names[i]))
)
{ /* This is a standard file */
int attrs = (*file_access == 'r' ? a_read+a_execute : a_write+a_execute);
stream *s = &std_files[i];
if ( attrs != std_file_attrs[i] )
return e_invalidaccess;
make_file(pfile, attrs, s);
/* If this is %lineedit or %statementedit, */
/* read a line now. */
switch ( i )
{
case 3: case 4:
{ uint count;
int code = zreadline_stdin(lineedit_buf,
lineedit_buf_size, &count);
if ( code < 0 ) return code;
sread_string(s, lineedit_buf, count);
s->can_close = -1;
return 0;
}
}
return 0;
}
return e_undefinedfilename;
}
/* Close inaccessible files just before a restore. */
void
file_restore(alloc_save *save)
{ while ( file_list != 0 &&
alloc_is_since_save((char *)file_list, save)
)
{ stream *s = file_list;
if ( s->can_close > 0 ) /* ignore std & buffered files */
{ sclose(s);
}
file_list = s->next;
if ( s->next != 0 ) s->next->prev = s->prev;
s_disable(s);
}
}